/**
 *****************************************************************************
 * @file     usb_core.c
 * @author   MCD Application Team
 * @version  V1.0.0
 * @date     11/29/2010
 * @brief    USB-OTG Core Layer
 ******************************************************************************
 * @copy
 *
 * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
 * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
 * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
 * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
 * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
 * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
 *
 * <h2><center>&copy; COPYRIGHT 2010 STMicroelectronics</center></h2>
 */

/* Includes ------------------------------------------------------------------*/

#include "usb_core.h"


/** @addtogroup USB_OTG_DRIVER
 * @{
 */

/** @defgroup USB_CORE
 * @brief This file includes the USB-OTG Core Layer
 * @{
 */


/** @defgroup USB_CORE_Private_Defines
 * @{
 */
/**
 * @}
 */


/** @defgroup USB_CORE_Private_TypesDefinitions
 * @{
 */
/**
 * @}
 */



/** @defgroup USB_CORE_Private_Macros
 * @{
 */
/**
 * @}
 */


/** @defgroup USB_CORE_Private_Variables
 * @{
 */
/**
 * @}
 */


/** @defgroup USB_CORE_Private_FunctionPrototypes
 * @{
 */


/**
 * @}
 */


/** @defgroup USB_CORE_Private_Functions
 * @{
 */



/**
 * @brief  USB_OTG_EnableCommonInt
 *         Initializes the commmon interrupts, used in both device and modes
 * @param  pdev : Selected device
 * @retval None
 */
static void USB_OTG_EnableCommonInt(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_GINTMSK_TypeDef int_mask;

    int_mask.d32 = 0;
    /* Clear any pending USB_OTG Interrupts */
    USB_OTG_WRITE_REG32( &pdev->regs.GREGS->GOTGINT, 0xFFFFFFFF);

    /* Clear any pending interrupts */
    USB_OTG_WRITE_REG32( &pdev->regs.GREGS->GINTSTS, 0xFFFFFFFF);

    /* Enable the interrupts in the INTMSK */
    int_mask.b.wkupintr   = 1;
    int_mask.b.usbsuspend = 1;

    USB_OTG_WRITE_REG32( &pdev->regs.GREGS->GINTMSK, int_mask.d32);
}

/**
 * @brief  USB_OTG_CoreReset : Soft reset of the core
 * @param  pdev : Selected device
 * @retval USB_OTG_STS : status
 */
static USB_OTG_STS USB_OTG_CoreReset(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_STS status = USB_OTG_OK;
    __IO USB_OTG_GRSTCTL_TypeDef greset;
    uint32_t count = 0;

    greset.d32 = 0;
    /* Wait for AHB master IDLE state. */
    do
    {
        USB_OTG_BSP_uDelay(3);
        greset.d32 = USB_OTG_READ_REG32(&pdev->regs.GREGS->GRSTCTL);
        if (++count > 200000)
        {
            return USB_OTG_OK;
        }
    }
    while (greset.b.ahbidle == 0);
    /* Core Soft Reset */
    count = 0;
    greset.b.csftrst = 1;
    USB_OTG_WRITE_REG32(&pdev->regs.GREGS->GRSTCTL, greset.d32 );
    do
    {
        greset.d32 = USB_OTG_READ_REG32(&pdev->regs.GREGS->GRSTCTL);
        if (++count > 200000)
        {
            break;
        }
    }
    while (greset.b.csftrst == 1);
    /* Wait for 3 PHY Clocks*/
    USB_OTG_BSP_uDelay(3);
    return status;
}

/**
 * @brief  USB_OTG_WritePacket : Writes a packet into the Tx FIFO associated
 *         with the EP
 * @param  pdev : Selected device
 * @param  src : source pointer
 * @param  ch_ep_num : end point number
 * @param  bytes : No. of bytes
 * @retval USB_OTG_STS : status
 */
USB_OTG_STS USB_OTG_WritePacket(USB_OTG_CORE_HANDLE *pdev,
                                uint8_t             *src,
                                uint8_t ch_ep_num,
                                uint16_t bytes)
{
    USB_OTG_STS status = USB_OTG_OK;

    uint32_t dword_count = 0, i = 0;
    __IO uint32_t *fifo;

    dword_count =  (bytes + 3) / 4;
    fifo = pdev->regs.DFIFO[ch_ep_num];
    for (i = 0; i < dword_count; i++, src += 4)
    {
        USB_OTG_WRITE_REG32( fifo, *((__packed uint32_t*)src) );
    }

    return status;
}


/**
 * @brief  USB_OTG_ReadPacket : Reads a packet from the Rx FIFO
 * @param  pdev : Selected device
 * @param  dest : Destination Pointer
 * @param  bytes : No. of bytes
 * @retval None
 */
void *USB_OTG_ReadPacket(USB_OTG_CORE_HANDLE *pdev,
                         uint8_t *dest,
                         uint16_t bytes)
{
    uint32_t i = 0;
    uint32_t word_count = (bytes + 3) / 4;
    __IO uint32_t *fifo = pdev->regs.DFIFO[0];

    for (i = 0; i < word_count; i++, dest += 4)
    {
        *(__packed uint32_t*)dest = USB_OTG_READ_REG32(fifo);

    }
    return (void*)dest;
}


/**
 * @brief  USB_OTG_CoreInit
 *         Initializes the USB_OTG controller registers and prepares the core
 *         device mode or host mode operation.
 * @param  pdev : Selected device
 * @retval USB_OTG_STS : status
 */
USB_OTG_STS USB_OTG_CoreInit(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_STS status = USB_OTG_OK;
    USB_OTG_GUSBCFG_TypeDef usbcfg;
    USB_OTG_GCCFG_TypeDef cfgctl;
    uint32_t i = 0;

    usbcfg.d32 = 0;
    cfgctl.d32 = 0;



    /* initialize device cfg following its address */
    pdev->cfg.host_channels    = USB_OTG_HOST_CHANNEL_NBR;
    pdev->cfg.TotalFifoSize    = USB_OTG_TOTAL_FIFO_SIZE;

    pdev->regs.GREGS = (USB_OTG_GREGS*)(USB_OTG_FS_BASE_ADDR + \
                                        USB_OTG_CORE_GLOBAL_REGS_OFFSET);

    pdev->regs.HREGS = (USB_OTG_HREGS*)(USB_OTG_FS_BASE_ADDR + \
                                        USB_OTG_HOST_GLOBAL_REG_OFFSET);
    pdev->regs.HPRT0 = (uint32_t*)(USB_OTG_FS_BASE_ADDR + USB_OTG_HOST_PORT_REGS_OFFSET);

    for (i = 0; i < pdev->cfg.host_channels; i++)
    {
        pdev->regs.HC_REGS[i] = (USB_OTG_HC_REGS*)(USB_OTG_FS_BASE_ADDR + \
                                                   USB_OTG_HOST_CHAN_REGS_OFFSET + \
                                                   (i * USB_OTG_CHAN_REGS_OFFSET));
    }
    for (i = 0; i < pdev->cfg.host_channels; i++)
    {
        pdev->regs.DFIFO[i] = (uint32_t*)(USB_OTG_FS_BASE_ADDR + USB_OTG_DATA_FIFO_OFFSET + \
                                          (i * USB_OTG_DATA_FIFO_SIZE));
    }
    pdev->regs.PCGCCTL = (uint32_t*)(USB_OTG_FS_BASE_ADDR + USB_OTG_PCGCCTL_OFFSET);

    USB_OTG_DisableGlobalInt(pdev);

    usbcfg.d32 = USB_OTG_READ_REG32(&pdev->regs.GREGS->GUSBCFG);;
    usbcfg.b.physel  = 1; /* FS Interface */
    USB_OTG_WRITE_REG32(&pdev->regs.GREGS->GUSBCFG, usbcfg.d32);
    /* Reset after a PHY select and set Host mode */
    USB_OTG_CoreReset(pdev);
    /* Deactivate the power down and enable the sensing*/
    cfgctl.d32 = 0;
    cfgctl.b.pwdn = 1;
    cfgctl.b.vbussensingA = 1;
    cfgctl.b.vbussensingB = 1;

#ifdef SOF_OUTPUT_ENABLED
    cfgctl.b.SOFouten = 1;
#endif

    USB_OTG_WRITE_REG32(&pdev->regs.GREGS->GCCFG, cfgctl.d32);
    USB_OTG_BSP_mDelay(20);
    /* Program GUSBCFG.OtgUtmifsSel to I2C*/
    usbcfg.d32 = USB_OTG_READ_REG32(&pdev->regs.GREGS->GUSBCFG);
    usbcfg.b.otgutmifssel = 0;

    USB_OTG_WRITE_REG32(&pdev->regs.GREGS->GUSBCFG, usbcfg.d32);
    return status;
}
/**
 * @brief  USB_OTG_EnableGlobalInt
 *         Enables the controller's Global Int in the AHB Config reg
 * @param  pdev : Selected device
 * @retval USB_OTG_STS : status
 */
USB_OTG_STS USB_OTG_EnableGlobalInt(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_STS status = USB_OTG_OK;
    USB_OTG_GAHBCFG_TypeDef ahbcfg;

    ahbcfg.d32 = 0;
    ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */
    USB_OTG_MODIFY_REG32(&pdev->regs.GREGS->GAHBCFG, 0, ahbcfg.d32);
    return status;
}


/**
 * @brief  USB_OTG_DisableGlobalInt
 *         Enables the controller's Global Int in the AHB Config reg
 * @param  pdev : Selected device
 * @retval USB_OTG_STS : status
 */
USB_OTG_STS USB_OTG_DisableGlobalInt(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_STS status = USB_OTG_OK;
    USB_OTG_GAHBCFG_TypeDef ahbcfg;

    ahbcfg.d32 = 0;
    ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */
    USB_OTG_MODIFY_REG32(&pdev->regs.GREGS->GAHBCFG, ahbcfg.d32, 0);
    return status;
}


/**
 * @brief  USB_OTG_FlushTxFifo : Flush a Tx FIFO
 * @param  pdev : Selected device
 * @param  num : FO num
 * @retval USB_OTG_STS : status
 */
USB_OTG_STS USB_OTG_FlushTxFifo(USB_OTG_CORE_HANDLE *pdev, uint32_t num )
{
    USB_OTG_STS status = USB_OTG_OK;
    __IO USB_OTG_GRSTCTL_TypeDef greset;

    uint32_t count = 0;

    greset.d32 = 0;
    greset.b.txfflsh = 1;
    greset.b.txfnum  = num;
    USB_OTG_WRITE_REG32( &pdev->regs.GREGS->GRSTCTL, greset.d32 );
    do
    {
        greset.d32 = USB_OTG_READ_REG32( &pdev->regs.GREGS->GRSTCTL);
        if (++count > 200000)
        {
            break;
        }
    }
    while (greset.b.txfflsh == 1);
    /* Wait for 3 PHY Clocks*/
    USB_OTG_BSP_uDelay(3);
    return status;
}


/**
 * @brief  USB_OTG_FlushRxFifo : Flush a Rx FIFO
 * @param  pdev : Selected device
 * @retval USB_OTG_STS : status
 */
USB_OTG_STS USB_OTG_FlushRxFifo( USB_OTG_CORE_HANDLE *pdev )
{
    USB_OTG_STS status = USB_OTG_OK;
    __IO USB_OTG_GRSTCTL_TypeDef greset;
    uint32_t count = 0;

    greset.d32 = 0;
    greset.b.rxfflsh = 1;
    USB_OTG_WRITE_REG32( &pdev->regs.GREGS->GRSTCTL, greset.d32 );
    do
    {
        greset.d32 = USB_OTG_READ_REG32( &pdev->regs.GREGS->GRSTCTL);
        if (++count > 200000)
        {
            break;
        }
    }
    while (greset.b.rxfflsh == 1);
    /* Wait for 3 PHY Clocks*/
    USB_OTG_BSP_uDelay(3);
    return status;
}


/**
 * @brief  USB_OTG_SetHostMode
 * @param  pdev : Selected device
 * @param  mode :
 * @retval USB_OTG_STS : status
 */
USB_OTG_STS USB_OTG_SetHostMode(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_STS status = USB_OTG_OK;
    USB_OTG_GUSBCFG_TypeDef usbcfg;

    usbcfg.d32 = USB_OTG_READ_REG32(&pdev->regs.GREGS->GUSBCFG);

    usbcfg.b.force_dev = 0;
    usbcfg.b.force_host = 1;

    USB_OTG_WRITE_REG32(&pdev->regs.GREGS->GUSBCFG, usbcfg.d32);
    USB_OTG_BSP_mDelay(50);
    return status;
}



/**
 * @brief  USB_OTG_ReadOtgItr : returns the USB_OTG Interrupt register
 * @param  pdev : Selected device
 * @retval Status
 */
uint32_t USB_OTG_ReadOtgItr(USB_OTG_CORE_HANDLE *pdev)
{
    return USB_OTG_READ_REG32(&pdev->regs.GREGS->GOTGINT);
}


/**
 * @brief  USB_OTG_CoreInitHost : Initializes USB_OTG controller for host mode
 * @param  pdev : Selected device
 * @retval status
 */
USB_OTG_STS USB_OTG_CoreInitHost(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_STS status = USB_OTG_OK;
    USB_OTG_FSIZ_TypeDef nptxfifosize;
    USB_OTG_FSIZ_TypeDef ptxfifosize;
    USB_OTG_HCCHAR_TypeDef hcchar;
    USB_OTG_HCFG_TypeDef hcfg;


    uint32_t num_channels = 0, i = 0;

    nptxfifosize.d32 = 0;
    ptxfifosize.d32 = 0;

    hcfg.d32 = 0;


    /* configure charge pump IO */
    USB_OTG_BSP_ConfigVBUS();

    /* Restart the Phy Clock */
    USB_OTG_WRITE_REG32(pdev->regs.PCGCCTL, 0);

    /* Initialize Host Configuration Register */
    USB_OTG_InitFSLSPClkSel(pdev, HCFG_48_MHZ);

    hcfg.d32 = USB_OTG_READ_REG32(&pdev->regs.HREGS->HCFG);
    hcfg.b.fslssupp = 1;
    USB_OTG_WRITE_REG32(&pdev->regs.HREGS->HCFG, hcfg.d32);

    /* Configure data FIFO sizes */
    /* Rx FIFO */
    USB_OTG_WRITE_REG32(&pdev->regs.GREGS->GRXFSIZ, RX_FIFO_SIZE);

    /* Tx FIFO */
    nptxfifosize.b.depth     = TXH_NP_FIFOSIZ;
    nptxfifosize.b.startaddr = RX_FIFO_SIZE;
    USB_OTG_WRITE_REG32(&pdev->regs.GREGS->HNPTXFSIZ, nptxfifosize.d32);

    ptxfifosize.b.startaddr = RX_FIFO_SIZE + TXH_NP_FIFOSIZ;
    ptxfifosize.b.depth     = TXH_P_FIFOSIZ;
    USB_OTG_WRITE_REG32(&pdev->regs.GREGS->HPTXFSIZ, ptxfifosize.d32);



    /* Make sure the FIFOs are flushed. */
    USB_OTG_FlushTxFifo(pdev, 0x10 );       /* all Tx FIFOs */
    USB_OTG_FlushRxFifo(pdev);


    /* Flush out any leftover queued requests. */
    num_channels = pdev->cfg.host_channels;

    for (i = 0; i < num_channels; i++)
    {
        hcchar.d32 = USB_OTG_READ_REG32(&pdev->regs.HC_REGS[i]->HCCHAR);
        hcchar.b.chen = 0;
        hcchar.b.chdis = 1;
        hcchar.b.epdir = 0;
        USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[i]->HCCHAR, hcchar.d32);

        /* Clear all pending HC Interrupts */
        USB_OTG_WRITE_REG32( &pdev->regs.HC_REGS[i]->HCINT, 0xFFFFFFFF );
    }
    /* Halt all channels to put them into a known state. */
    for (i = 0; i < num_channels; i++)
    {
        hcchar.d32 = USB_OTG_READ_REG32(&pdev->regs.HC_REGS[i]->HCCHAR);
        hcchar.b.chen = 1;
        hcchar.b.chdis = 1;
        hcchar.b.epdir = 0;
        USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[i]->HCCHAR, hcchar.d32);
        do
        {
            USB_OTG_READ_REG32(&pdev->regs.GREGS->GRXSTSP);
            hcchar.d32 = USB_OTG_READ_REG32(&pdev->regs.HC_REGS[i]->HCCHAR);
            USB_OTG_BSP_uDelay(20);
        }
        while (hcchar.b.chen);
    }
    /* Disable HALT interrupt Masks */
    for (i = 0; i < num_channels; i++)
    {
        USB_OTG_HCGINTMSK_TypeDef hcintmsk;
        hcintmsk.d32 = USB_OTG_READ_REG32(&pdev->regs.HC_REGS[i]->HCGINTMSK);
        hcintmsk.b.chhltd = 0;
        USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[i]->HCGINTMSK, hcintmsk.d32);
    }

    USB_OTG_DriveVbus(pdev, 1);


    USB_OTG_EnableHostInt(pdev);
    return status;
}


/**
 * @brief  USB_OTG_EnableHostInt: Enables the Host mode interrupts
 * @param  pdev : Selected device
 * @retval USB_OTG_STS : status
 */
USB_OTG_STS USB_OTG_EnableHostInt(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_STS status = USB_OTG_OK;
    USB_OTG_GINTMSK_TypeDef intmsk;

    intmsk.d32 = 0;
    /* Disable all interrupts. */
    USB_OTG_WRITE_REG32(&pdev->regs.GREGS->GINTMSK, 0);

    /* Clear any pending interrupts. */
    USB_OTG_WRITE_REG32(&pdev->regs.GREGS->GINTSTS, 0xFFFFFFFF);

    /* Enable the common interrupts */
    USB_OTG_EnableCommonInt(pdev);


    intmsk.b.rxstsqlvl  = 1;
    intmsk.b.portintr   = 1;
    intmsk.b.hcintr     = 1;
    intmsk.b.disconnect = 1;
    intmsk.b.sofintr    = 0;
    intmsk.b.incomplisoout  = 0;
    USB_OTG_MODIFY_REG32(&pdev->regs.GREGS->GINTMSK, intmsk.d32, intmsk.d32);
    return status;
}

/**
 * @brief  USB_OTG_InitFSLSPClkSel : Initializes the FSLSPClkSel field of the
 *         HCFG register on the PHY type
 * @param  pdev : Selected device
 * @param  freq : clock frequency
 * @retval None
 */
void USB_OTG_InitFSLSPClkSel(USB_OTG_CORE_HANDLE *pdev, uint8_t freq)
{
    USB_OTG_HCFG_TypeDef hcfg;

    hcfg.d32 = USB_OTG_READ_REG32(&pdev->regs.HREGS->HCFG);
    hcfg.b.fslspclksel = freq;
    USB_OTG_WRITE_REG32(&pdev->regs.HREGS->HCFG, hcfg.d32);
}


/**
 * @brief  USB_OTG_ReadHPRT0 : Reads HPRT0 to modify later
 * @param  pdev : Selected device
 * @retval HPRT0 value
 */
uint32_t USB_OTG_ReadHPRT0(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_HPRT0_TypeDef hprt0;

    hprt0.d32 = USB_OTG_READ_REG32(pdev->regs.HPRT0);
    hprt0.b.prtena = 0;
    hprt0.b.prtconndet = 0;
    hprt0.b.prtenchng = 0;
    hprt0.b.prtovrcurrchng = 0;
    return hprt0.d32;
}


/**
 * @brief  USB_OTG_ReadHostAllChannels_intr : Register PCD Callbacks
 * @param  pdev : Selected device
 * @retval Status
 */
uint32_t USB_OTG_ReadHostAllChannels_intr(USB_OTG_CORE_HANDLE *pdev)
{
    return USB_OTG_READ_REG32(&pdev->regs.HREGS->HAINT);
}


/**
 * @brief  USB_OTG_ResetPort : Reset Host Port
 * @param  pdev : Selected device
 * @retval status
 *   before clearing the reset bit.
 */
uint32_t USB_OTG_ResetPort(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_HPRT0_TypeDef hprt0;

    hprt0.d32 = USB_OTG_ReadHPRT0(pdev);
    hprt0.b.prtrst = 1;
    USB_OTG_WRITE_REG32(pdev->regs.HPRT0, hprt0.d32);
    /* USB Reset time */
    USB_OTG_BSP_mDelay(30);
    hprt0.b.prtrst = 0;
    USB_OTG_WRITE_REG32(pdev->regs.HPRT0, hprt0.d32);
    /* reset recovery time */
    USB_OTG_BSP_mDelay(10);
    return 1;
}


/**
 * @brief  USB_OTG_HC_Init : Prepares a host channel for transferring packets
 * @param  pdev : Selected device
 * @param  hc_num : channel number
 * @retval USB_OTG_STS : status
 */
USB_OTG_STS USB_OTG_HC_Init(USB_OTG_CORE_HANDLE *pdev, uint8_t hc_num)
{
    USB_OTG_STS status = USB_OTG_OK;
    uint32_t intr_enable = 0;
    USB_OTG_HCGINTMSK_TypeDef hcintmsk;
    USB_OTG_GINTMSK_TypeDef gintmsk;
    USB_OTG_HCCHAR_TypeDef hcchar;


    gintmsk.d32 = 0;
    hcintmsk.d32 = 0;
    hcchar.d32 = 0;

    /* Clear old interrupt conditions for this host channel. */
    hcintmsk.d32 = 0xFFFFFFFF;
    USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[hc_num]->HCINT, hcintmsk.d32);

    /* Enable channel interrupts required for this transfer. */
    hcintmsk.d32 = 0;

    switch (pdev->host.hc[hc_num].ep_type)
    {
    case EP_TYPE_CTRL:
    case EP_TYPE_BULK:
        hcintmsk.b.xfercompl = 1;
        hcintmsk.b.stall = 1;
        hcintmsk.b.xacterr = 1;
        hcintmsk.b.datatglerr = 1;
        hcintmsk.b.nak = 1;
        if (pdev->host.hc[hc_num].ep_is_in)
        {
            hcintmsk.b.bblerr = 1;
        }
        break;
    case EP_TYPE_INTR:
        hcintmsk.b.xfercompl = 1;
        hcintmsk.b.nak = 1;
        hcintmsk.b.stall = 1;
        hcintmsk.b.xacterr = 1;
        hcintmsk.b.datatglerr = 1;
        hcintmsk.b.frmovrun = 1;

        if (pdev->host.hc[hc_num].ep_is_in)
        {
            hcintmsk.b.bblerr = 1;
        }

        break;
    case EP_TYPE_ISOC:
        hcintmsk.b.xfercompl = 1;
        hcintmsk.b.frmovrun = 1;
        hcintmsk.b.ack = 1;

        if (pdev->host.hc[hc_num].ep_is_in)
        {
            hcintmsk.b.xacterr = 1;
            hcintmsk.b.bblerr = 1;
        }
        break;
    }

    USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[hc_num]->HCGINTMSK, hcintmsk.d32);


    /* Enable the top level host channel interrupt. */
    intr_enable = (1 << hc_num);
    USB_OTG_MODIFY_REG32(&pdev->regs.HREGS->HAINTMSK, 0, intr_enable);

    /* Make sure host channel interrupts are enabled. */
    gintmsk.b.hcintr = 1;
    USB_OTG_MODIFY_REG32(&pdev->regs.GREGS->GINTMSK, 0, gintmsk.d32);

    /* Program the HCCHAR register */
    hcchar.d32 = 0;
    hcchar.b.devaddr = pdev->host.hc[hc_num].dev_addr;
    hcchar.b.epnum   = pdev->host.hc[hc_num].ep_num;
    hcchar.b.epdir   = pdev->host.hc[hc_num].ep_is_in;
    hcchar.b.lspddev = (pdev->host.hc[hc_num].speed == HPRT0_PRTSPD_LOW_SPEED);
    hcchar.b.eptype  = pdev->host.hc[hc_num].ep_type;
    hcchar.b.mps     = pdev->host.hc[hc_num].max_packet;
    if (pdev->host.hc[hc_num].ep_type == HCCHAR_INTR)
    {
        hcchar.b.oddfrm  = 1;
    }
    USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[hc_num]->HCCHAR, hcchar.d32);
    return status;
}


/**
 * @brief  USB_OTG_HC_StartXfer : Start transfer
 * @param  pdev : Selected device
 * @param  hc_num : channel number
 * @retval USB_OTG_STS : status
 */
USB_OTG_STS USB_OTG_HC_StartXfer(USB_OTG_CORE_HANDLE *pdev, uint8_t hc_num)
{
    USB_OTG_STS status = USB_OTG_OK;
    USB_OTG_HCCHAR_TypeDef hcchar;
    USB_OTG_HCTSIZn_TypeDef hctsiz;
    USB_OTG_GNPTXSTS_TypeDef hnptxsts;
    USB_OTG_HPTXSTS_TypeDef hptxsts;
    USB_OTG_GINTMSK_TypeDef intmsk;
    uint16_t len_words = 0;

    uint16_t num_packets;
    uint16_t max_hc_pkt_count;

    max_hc_pkt_count = 256;
    hctsiz.d32 = 0;
    hcchar.d32 = 0;
    intmsk.d32 = 0;

    /* Compute the expected number of packets associated to the transfer */
    if (pdev->host.hc[hc_num].xfer_len > 0)
    {
        num_packets = (pdev->host.hc[hc_num].xfer_len + \
                       pdev->host.hc[hc_num].max_packet - 1) / pdev->host.hc[hc_num].max_packet;

        if (num_packets > max_hc_pkt_count)
        {
            num_packets = max_hc_pkt_count;
            pdev->host.hc[hc_num].xfer_len = num_packets * \
                                             pdev->host.hc[hc_num].max_packet;
        }
    }
    else
    {
        num_packets = 1;
    }
    if (pdev->host.hc[hc_num].ep_is_in)
    {
        pdev->host.hc[hc_num].xfer_len = num_packets * \
                                         pdev->host.hc[hc_num].max_packet;
    }
    /* Initialize the HCTSIZn register */
    hctsiz.b.xfersize = pdev->host.hc[hc_num].xfer_len;
    hctsiz.b.pktcnt = num_packets;
    hctsiz.b.pid = pdev->host.hc[hc_num].data_pid;
    USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[hc_num]->HCTSIZ, hctsiz.d32);


    hcchar.d32 = USB_OTG_READ_REG32(&pdev->regs.HC_REGS[hc_num]->HCCHAR);
    hcchar.b.oddfrm = USB_OTG_IsEvenFrame(pdev);

    /* Set host channel enable */
    hcchar.b.chen = 1;
    hcchar.b.chdis = 0;
    USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[hc_num]->HCCHAR, hcchar.d32);

    if (!pdev->host.hc[hc_num].ep_is_in && pdev->host.hc[hc_num].xfer_len > 0)
    {
        switch (pdev->host.hc[hc_num].ep_type)
        {
        /* Non periodic transfer */
        case EP_TYPE_CTRL:
        case EP_TYPE_BULK:
            hnptxsts.d32 = USB_OTG_READ_REG32(&pdev->regs.GREGS->GNPTXSTS);
            len_words = (pdev->host.hc[hc_num].max_packet + 3) / 4;

            /* check if at least 1 MPS FIFO space available */
            if (len_words > hnptxsts.b.nptxfspcavail)
            {
                /* need to process data in nptxfempty interrupt */
                intmsk.b.nptxfempty = 1;
                USB_OTG_MODIFY_REG32( &pdev->regs.GREGS->GINTMSK, 0, intmsk.d32);
            }
            else
            {
                /*write packet into the Tx FIFO. */
                USB_OTG_WritePacket(pdev,
                                    pdev->host.hc[hc_num].xfer_buff,
                                    hc_num, pdev->host.hc[hc_num].xfer_len);
            }

            break;
        /* Periodic transfer */
        case EP_TYPE_INTR:
        case EP_TYPE_ISOC:
            hptxsts.d32 = USB_OTG_READ_REG32(&pdev->regs.HREGS->HPTXSTS);
            len_words = (pdev->host.hc[hc_num].max_packet + 3) / 4;
            /* check if at least 1 MPS FIFO space available */
            if (len_words > hptxsts.b.ptxfspcavail)
            {
                /* need to process data in ptxfempty interrupt */
                intmsk.b.ptxfempty = 1;
                USB_OTG_MODIFY_REG32( &pdev->regs.GREGS->GINTMSK, 0, intmsk.d32);
            }
            else
            {

                /* Write packet into the Tx FIFO. */
                USB_OTG_WritePacket(pdev,
                                    pdev->host.hc[hc_num].xfer_buff,
                                    hc_num, pdev->host.hc[hc_num].xfer_len);
            }
            break;

        default:
            break;
        }

    }
    return status;
}


/**
 * @brief  USB_OTG_HC_Halt : Halt channel
 * @param  pdev : Selected device
 * @param  hc_num : channel number
 * @retval USB_OTG_STS : status
 */
USB_OTG_STS USB_OTG_HC_Halt(USB_OTG_CORE_HANDLE *pdev, uint8_t hc_num)
{
    USB_OTG_STS status = USB_OTG_OK;
    USB_OTG_GNPTXSTS_TypeDef nptxsts;
    USB_OTG_HPTXSTS_TypeDef hptxsts;
    USB_OTG_HCCHAR_TypeDef hcchar;

    nptxsts.d32 = 0;
    hptxsts.d32 = 0;
    hcchar.d32 = USB_OTG_READ_REG32(&pdev->regs.HC_REGS[hc_num]->HCCHAR);
    hcchar.b.chen = 1;
    hcchar.b.chdis = 1;

    /* Check for space in the request queue to issue the halt. */
    if (hcchar.b.eptype == HCCHAR_CTRL || hcchar.b.eptype == HCCHAR_BULK)
    {
        nptxsts.d32 = USB_OTG_READ_REG32(&pdev->regs.GREGS->GNPTXSTS);
        if (nptxsts.b.nptxqspcavail == 0)
        {
            hcchar.b.chen = 0;
        }
    }
    else
    {
        hptxsts.d32 = USB_OTG_READ_REG32(&pdev->regs.HREGS->HPTXSTS);
        if (hptxsts.b.ptxqspcavail == 0)
        {
            hcchar.b.chen = 0;
        }
    }
    USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[hc_num]->HCCHAR, hcchar.d32);
    return status;
}


/**
 * @brief  USB_OTG_DriveVbus : set/reset vbus
 * @param  pdev : Selected device
 * @param  state : VBUS state
 * @retval None
 */
void USB_OTG_DriveVbus(USB_OTG_CORE_HANDLE *pdev, uint8_t state)
{
    USB_OTG_HPRT0_TypeDef hprt0;

    hprt0.d32 = 0;

    /* enable disable the external charge pump */
    USB_OTG_BSP_DriveVBUS(state);

    /* Turn on the Host port power. */
    hprt0.d32 = USB_OTG_ReadHPRT0(pdev);
    if ((hprt0.b.prtpwr == 0 ) && (state == 1 ))
    {
        hprt0.b.prtpwr = 1;
        USB_OTG_WRITE_REG32(pdev->regs.HPRT0, hprt0.d32);
    }
    if ((hprt0.b.prtpwr == 1 ) && (state == 0 ))
    {
        hprt0.b.prtpwr = 0;
        USB_OTG_WRITE_REG32(pdev->regs.HPRT0, hprt0.d32);
    }

    USB_OTG_BSP_mDelay(200);
}

/**
 * @brief  USB_OTG_RestoreClock : Restore default clock configuration
 * @param  pdev : Selected device
 * @retval None
 */
void USB_OTG_RestoreClock(USB_OTG_CORE_HANDLE *pdev)
{
    USB_OTG_PCGCCTL_TypeDef power;

    /* restore full power */
    power.d32 = USB_OTG_READ_REG32(&pdev->regs.PCGCCTL);
    power.b.gatehclk = 0;
    power.b.stoppclk = 0;
    USB_OTG_WRITE_REG32(pdev->regs.PCGCCTL, power.d32);
}

/**
 * @brief  USB_OTG_IsEvenFrame
 *         This function returns the frame number for sof packet
 * @param  pdev : Selected device
 * @retval Frame number
 */
uint8_t USB_OTG_IsEvenFrame(USB_OTG_CORE_HANDLE *pdev)
{
    return !(USB_OTG_READ_REG32(&pdev->regs.HREGS->HFNUM) & 0x1);
}


/**
 * @brief  Stop Host mode
 * @param  Selected device
 * @retval : None
 */
void USB_OTG_StopHostMode(USB_OTG_CORE_HANDLE *pdev)
{
    uint32_t num_channels, i;

    USB_OTG_WRITE_REG32(&pdev->regs.HREGS->HAINTMSK, 0);
    USB_OTG_WRITE_REG32(&pdev->regs.HREGS->HAINT,      0xFFFFFFFF);


    /* Flush out any leftover queued requests. */
    num_channels = 8;

    for (i = 0; i < num_channels; i++)
    {
        /* Halt channel i */
        USB_OTG_HC_Halt(pdev, i);
        /* Clear all pending HC Interrupts */
        USB_OTG_WRITE_REG32( &pdev->regs.HC_REGS[i]->HCINT, 0xFFFFFFFF );
    }

    /* Flush the Tx and Rx FIFO */
    USB_OTG_FlushTxFifo(pdev,  0x10 );
    USB_OTG_FlushRxFifo(pdev);
}


#ifdef __CC_ARM
#pragma O0
#endif
/**
 * @brief  USB_OTG_ReadCoreItr : returns the Core Interrupt register
 * @param  pdev : Selected device
 * @retval Status
 */

uint32_t USB_OTG_ReadCoreItr(USB_OTG_CORE_HANDLE *pdev)
{
    uint32_t v = 0;

    v = USB_OTG_READ_REG32(&pdev->regs.GREGS->GINTSTS);
    v &= USB_OTG_READ_REG32(&pdev->regs.GREGS->GINTMSK);
    return v;
}


/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */

/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/
